extern "C"
{
#include "clone_detect.h"
}
#include "CppUTest/UtestMacros.h"
#include "CppUTest/TestHarness.h"
extern options_t global_options;
extern duplicate_result_t dup_result;

TEST_GROUP( CloneDetectGroup )
{
    void setup()
    {
        memset( &global_options, 0, sizeof( options_t ));
        global_options.min_dup_line = 5;
    }
};

TEST( CloneDetectGroup, ReadArgsTest)
{
    int argc;
    char *argv[5];
    size_t strlen = 20;

    for( int i = 0; i < 5; i++)
        argv[i] = (char *)malloc( strlen );

    argc = 1;
    strncpy( argv[0], "./clone_detect", strlen );
    CHECK_FALSE( read_args( argc, argv ));

    argc = 2;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    CHECK( read_args( argc, argv ));
    STRCMP_EQUAL( argv[1], global_options.dir  );

    argc = 3;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-c", strlen );
    CHECK( read_args( argc, argv ));
    CHECK( global_options.ignore_comments );

    argc = 3;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-b", strlen );
    CHECK( read_args( argc, argv ));
    CHECK( global_options.ignore_braces );

    argc = 3;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-j", strlen );
    CHECK_FALSE( read_args( argc, argv ));
    CHECK_FALSE( global_options.json_output );

    argc = 4;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-j", strlen );
    strncpy( argv[3], "clone_detect.json", strlen );
    CHECK_TRUE( read_args( argc, argv ));
    CHECK( global_options.json_output );
    STRCMP_EQUAL( argv[3], global_options.json_file );

    argc = 4;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-n", strlen );
    strncpy( argv[3], "-1", strlen );
    CHECK_TRUE( read_args( argc, argv ));
    CHECK( global_options.json_output );

    argc = 4;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-n", strlen );
    strncpy( argv[3], "2", strlen );
    CHECK_TRUE( read_args( argc, argv ));
    CHECK_EQUAL( 2, global_options.min_dup_line );

    argc = 5;
    strncpy( argv[0], "./clone_detect", strlen );
    strncpy( argv[1], "src", strlen );
    strncpy( argv[2], "-n", strlen );
    strncpy( argv[3], "2", strlen );
    strncpy( argv[4], "-v", strlen );
    CHECK_TRUE( read_args( argc, argv ));
    CHECK_TRUE( global_options.verbose );

    for( int i = 0; i < 5; i++)
        free( argv[i] );
}

TEST( CloneDetectGroup, ReadDirTest)
{
    char dir_name[ MAX_FILE_NAME_LEN ] = "../test_input";
    src_file_list_t  src_file_list;
    memset( &src_file_list, 0, sizeof( src_file_list_t ));
    /* directory have 3 code file, but current program only read two of them */
    CHECK_EQUAL( 2, read_dir( dir_name, &src_file_list ));
    STRCMP_EQUAL( "../test_input/ut_test_src.c", src_file_list.src_file[0].file_name );
    STRCMP_EQUAL( "../test_input/ut_test_src_braces.c", src_file_list.src_file[1].file_name );
}

TEST( CloneDetectGroup, ProcessFileTest)
{
    src_file_t src1;

    /* Check can not open file */
    strncpy( src1.file_name, "a.c", MAX_FILE_NAME_LEN );
    CHECK_FALSE( process_file( &src1 ));

    /* Check simplified result
     * ut_test_src.c have total 11 line and one is blank line */
    strncpy( src1.file_name, "../test_input/ut_test_src.c", MAX_FILE_NAME_LEN );
    CHECK( process_file( &src1 ));
    CHECK_EQUAL( 10 , src1.simplify_line_no );
    CHECK_EQUAL( 11 , src1.file_content[src1.simplify_line_no - 1].line_no );
    STRCMP_EQUAL( "(((s)&0x0300)>>((n)+6)))" , src1.file_content[src1.simplify_line_no - 1].line );

    /* Check simplified result
     * ut_test_src_comments.c have total 11 line and comments */
    global_options.ignore_comments = true;
    strncpy( src1.file_name, "../test_input/ut_test_src_comments.c", MAX_FILE_NAME_LEN );
    CHECK( process_file( &src1 ));
    CHECK_EQUAL( 44 , src1.simplify_line_no );
    CHECK_EQUAL( 65 , src1.file_content[src1.simplify_line_no - 1].line_no );
    global_options.ignore_comments = false;

    /* Check simplified result
     * ut_test_src_braces.c have total 11 line and braces : if() { 
     * brace should be consulted as one line when -b given */
    global_options.ignore_braces = true;
    strncpy( src1.file_name, "../test_input/ut_test_src_braces.c", MAX_FILE_NAME_LEN );
    CHECK( process_file( &src1 ));
    CHECK_EQUAL( 53 , src1.simplify_line_no );
    CHECK_EQUAL( 66 , src1.file_content[src1.simplify_line_no - 1].line_no );
    global_options.ignore_braces = false;
}

TEST( CloneDetectGroup, DetectBetweenFilesTest)
{
    src_file_t src1;
    src_file_t src2;

    global_options.min_dup_line = 2;
    strncpy( src1.file_name, "a.c", MAX_FILE_NAME_LEN );
    strncpy( src2.file_name, "b.c", MAX_FILE_NAME_LEN );
    src1.simplify_line_no = 3;
    src2.simplify_line_no = 3;
    strncpy( src1.file_content[0].line, "AAAA", MAX_LEN );
    src1.file_content[0].line_no = 0;
    strncpy( src1.file_content[1].line, "BBBB", MAX_LEN );
    src1.file_content[1].line_no = 1;
    strncpy( src1.file_content[2].line, "CCCC", MAX_LEN );
    src1.file_content[2].line_no = 2;
    strncpy( src2.file_content[0].line, "AAAA", MAX_LEN );
    src2.file_content[0].line_no = 0;
    strncpy( src2.file_content[1].line, "BBBB", MAX_LEN );
    src2.file_content[1].line_no = 1;
    strncpy( src2.file_content[2].line, "CCCC", MAX_LEN );
    src2.file_content[2].line_no = 2;

    /* Case 1, dieffernt soure file src1 and src2 */
    memset( &dup_result, 0, sizeof( duplicate_result_t ));
    detect_between_files( &src1, &src2 );
    CHECK_EQUAL( 6, dup_result.total_line );

   
    /* Case 2, detect in single file src1*/
    memset( &dup_result, 0, sizeof( duplicate_result_t ));
    detect_between_files( &src1, &src1 );
    CHECK_EQUAL( 0, dup_result.total_line );

    /* Case 3, src1 and src2 are different , src2 larger than src1
     * src1
     * AAAA\nBBBB\nCCCC
     * src2
     * AAAA\nBBBB\nCCCC\nDDDD
     */
    src2.simplify_line_no = 4;
    strncpy( src2.file_content[3].line, "CCCC", MAX_LEN );
    src2.file_content[3].line_no = 3;
    memset( &dup_result, 0, sizeof( duplicate_result_t ));
    detect_between_files( &src1, &src2 );
    CHECK_EQUAL( 6, dup_result.total_line );

    /* Case 4, src1 and src2 are different , part src1 match src2
     * src1
     * DDDD\nAAAA\nBBBB
     * src2
     * AAAA\nBBBB\nCCCC\nDDDD
     */
    strncpy( src1.file_content[0].line, "DDDD", MAX_LEN );
    src1.file_content[0].line_no = 0;
    strncpy( src1.file_content[1].line, "AAAA", MAX_LEN );
    src1.file_content[1].line_no = 1;
    strncpy( src1.file_content[2].line, "BBBB", MAX_LEN );
    src1.file_content[2].line_no = 2;
    memset( &dup_result, 0, sizeof( duplicate_result_t ));
    detect_between_files( &src1, &src2 );
    CHECK_EQUAL( 4, dup_result.total_line );
}

TEST( CloneDetectGroup, RecordTest )
{
    src_file_t src1;
    src_file_t src2;

    strncpy( src1.file_name, "a.c", MAX_FILE_NAME_LEN );
    strncpy( src2.file_name, "b.c", MAX_FILE_NAME_LEN );
    src1.file_content[0].line_no = 0;
    src1.file_content[1].line_no = 1;
    src1.file_content[2].line_no = 2;
    src2.file_content[0].line_no = 0;
    src2.file_content[1].line_no = 1;
    src2.file_content[2].line_no = 2;

    src1.simplify_line_no = 3;
    src2.simplify_line_no = 3;

    CHECK_FALSE( record( &src1, &src2, 3, 2, 1, 2 ));

    CHECK_FALSE( record( &src1, &src2, 1, 2, 65535, 2 ));

    for( int i = 0; i < MAX_DETAIL_FOR_EACH_RESULT; i++ )
        CHECK( record( &src1, &src2, 0, 2, 0, 2 ));
    CHECK_FALSE( record( &src1, &src2, 0, 2, 0, 2 ));

}
